﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

using ESRI.ArcGIS.Client.Geometry;
using ESRI.ArcGIS.Client;

using TouchToolkit.Framework;
using TouchToolkit.Framework.TouchInputProviders;
using TouchToolkit.GestureProcessor.ReturnTypes;
using TouchToolkit.GestureProcessor.Gesture_Definitions;
using TouchToolkit.GestureProcessor.Feedbacks.TouchFeedbacks;


using Microsoft.Surface;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
using Microsoft.Surface.Presentation.Input;

namespace GISforTT_API
{
    /// <summary>
    /// The layer that holds MapFrames, GhostFrames, and BookmarkListFrames.
    /// This class contains functions that allows the creation and deletion of the above objecs.
    /// This is a singleton class.
    /// </summary>
    public partial class BackgroundMapLayer : UserControl
    {
        #region members and containers

        // List of the MapFrames which are placed on the layer
        private List<MapFrame> mapFramesList;
        // List of the ghost frames which are placed on the layer
        private List<GhostFrame> ghostFramesList;
        // List of Bookmark list frames which are placed on the layer
        private List<BookmarkListFrame> bookmarkListFramesList;

        private MapLayersMangerPanel _mapLayersManagerPanel;
        
        double _width, _height;
        Orientation mapOrientation = Orientation.Horizontal;
        bool allowManipulation = false;

        #endregion


        /// <summary>
        /// Returns the instance of this singleton class.
        /// </summary>
        public static BackgroundMapLayer Instance { get; private set; }
        
        static BackgroundMapLayer() { Instance = new BackgroundMapLayer(); }
                
        // Private Constructor
        BackgroundMapLayer()
        {
            InitializeComponent();

            /* Adding the Map Layers Manager Panel in hidden mode */
            _mapLayersManagerPanel = new MapLayersMangerPanel();
            _mapLayersManagerPanel.Orientation = 270.0;
            _mapLayersManagerPanel.Center = new Point(400, 400);
            _mapLayersManagerPanel.Visibility = Visibility.Hidden;
            BackgroundScatterView.Items.Add(_mapLayersManagerPanel);
            
            // initialize the lists
            mapFramesList = new List<MapFrame>();
            ghostFramesList = new List<GhostFrame>();
            bookmarkListFramesList = new List<BookmarkListFrame>();


            if (!allowManipulation)
                GestureFramework.EventManager.AddEvent(ScatterCanvas, Gestures.Lasso, BackgroundMapLayer_Lasso);

        }

        /// <summary>
        /// Define some properties for the layout of the application background map.
        /// </summary>
        /// <param name="width">Width of the background map</param>
        /// <param name="height">Height of the background map</param>
        /// <param name="or">Orientation of the background map with respect to the screen.</param>
        public void setLayoutProperties(double width, double height, Orientation or)
        {
            mapOrientation = or;

            if (mapOrientation == Orientation.Vertical)
            {
                // Rotate the canvas
                ScatterCanvas.LayoutTransform = new RotateTransform(90);

                /* Must invert width and height since the background map is turned 90 deg */
                _width = height;
                _height = width;
            }
            else
            {
                _width = width;
                _height = height;
            }
                        
            ScatterCanvas.Height = _height;
            ScatterCanvas.Width = _width;

            BackgroundScatterView.Width = _width;
            BackgroundScatterView.Height = _height;
                        
        }

        /// <summary>
        /// Allow/disallow panning and zooming of background map
        /// </summary>
        /// <param name="allowManip">True to allow the manipulation</param>
        public void allowManipulationOfBgMap(bool allowManip)
        {
            allowManipulation = allowManip;
        }

                
        /// <summary>
        /// This has to be done after the object from this class is created
        /// </summary>
        public void InitializeBackgroundMap(Envelope BackgroundMapExtent)
        {
            /* Associate the background map with this BackgroundMapLayer as its parent */
            BackgroundMap.setIsABackgroundMap(true);

            /* make map full screen */
            BackgroundMap.Height = _height;
            BackgroundMap.Width = _width;

            BackgroundMap.CanPan = allowManipulation;
            BackgroundMap.CanZoom = allowManipulation;

            /* Setting the background of the screen to the map of Calgary */
            if (BackgroundMapExtent != null)
                BackgroundMap.setExtentFromEnvelop(BackgroundMapExtent);
            
            BackgroundMap.CreateBookmarkPins(BookmarkList.Instance.getBookmarks());
             
            // Setup for the non displayed layers map
            // NonDisplayedLayersMap is intended for layers that communicate with a server and are not displayed themselves
            NonDisplayedLayersMap.Height = _height;
            NonDisplayedLayersMap.Width = _width;
            if (BackgroundMapExtent != null)
                NonDisplayedLayersMap.Extent = BackgroundMapExtent;

            
        }

        /// <summary>
        /// Returns the background map object.
        /// </summary>
        /// <returns></returns>
        public ArcGisMap getBackgroundMap()
        {
            return BackgroundMap;
        }

        /// <summary>
        /// Handles the event when the background map is loaded.
        /// When the background map is loaded, functions that
        /// rely on the final position of the background map relative
        /// to the screen can be called.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        protected void BackgroundMap_Loaded(object sender, RoutedEventArgs e)
        {
            // Nothing here yet.
        }
                
        
        private void BackgroundMapLayer_Lasso(UIElement sender, GestureEventArgs e)
        {
            TouchPoints pts = e.Values.Get<TouchPoints>();

            int middlePt = (int)Math.Floor((decimal)(pts.Count / 2));
            Point scrPoint1 = new Point(pts[0].Y, BackgroundScatterView.Height - pts[0].X);
            Point scrPoint2 = new Point(pts[middlePt].Y, BackgroundScatterView.Height - pts[middlePt].X);

            MapPoint mapPoint1 = BackgroundMap.Map.ScreenToMap(scrPoint1);
            MapPoint mapPoint2 = BackgroundMap.Map.ScreenToMap(scrPoint2);

            Point point1 = BackgroundMap.Map.MapToScreen(mapPoint1);
            Point point2 = BackgroundMap.Map.MapToScreen(mapPoint2);

            IInputElement iie = BackgroundScatterView.InputHitTest(point1);

            //only create the frame if the origin of the lasso gesture was not drawn on an existing screen element
            if (iie == null)
            {
                MapFrame mf = MapFrameFactory.Instance.CreateMapFrame(mapPoint1, mapPoint2, scrPoint1, scrPoint2);
                AddMapFrame(mf);
            }




        }


        public Map MyNonDisplayedLayersMap  
        { get{ return NonDisplayedLayersMap; } }

        #region Adding and removing scatter Items

        /// <summary>
        /// Create and add a custom map frame showing the map inside the rectangle defined by the two screen points.
        /// </summary>
        /// <param name="point">first screen point - first corner of the map frame</param>
        /// <param name="point_2">second screen point - second corner of the map frame</param>
        public void AddMapFrame(Point point1, Point point2)
        {
            MapFrame customFrame = MapFrameFactory.Instance.CreateMapFrame(point1, point2);
            AddMapFrame(customFrame);
        }

        /// <summary>
        /// Search for the MapFrame in this layer that is related to the
        /// given Bookmark.  If the MapFrame exists, return it, otherwise, return
        /// null.
        /// </summary>
        /// <param name="bookmark">Bookmark object related to the requested map frame.</param>
        /// <returns>the map frame if found, otherwise null</returns>
        public MapFrame GetMapFrame(Bookmark bookmark)
        {
            for (int i = 0; i < mapFramesList.Count; i++)
            {
                if (mapFramesList[i] is MapFrame)
                {
                    MapFrame mp = mapFramesList[i] as MapFrame;
                    if (mp.RelatedBookmark == bookmark)
                        return mp;
                }
            }
            return null;
        }

        /// <summary>
        /// Add a map frame/window on top of the background map
        /// </summary>
        /// <param name="mFrame">The map frame to be added</param>        
        public void AddMapFrame(MapFrame mFrame)
        {
            if (mFrame == null) return;

            ghostFramesList.Add(mFrame.GetGhostFrame());
            mapFramesList.Add(mFrame);

            BackgroundScatterView.Items.Add(mFrame.GetGhostFrame());
            BackgroundScatterView.Items.Add(mFrame);
        }
        
        /// <summary>
        /// Removes a MapFrame, if it already exists
        /// </summary>
        /// <param name="deleteMap">The map frame to be deleted</param>
        public void RemoveMap(MapFrame deleteMap)
        {
            GhostFrame gf = deleteMap.GetGhostFrame();

            if (mapFramesList.Contains(deleteMap))
            {
                mapFramesList.Remove(deleteMap);
                ghostFramesList.Remove(gf);
            }

            if (BackgroundScatterView.Items.Contains(deleteMap))
                BackgroundScatterView.Items.Remove(deleteMap);
            if (BackgroundScatterView.Items.Contains(gf))
                BackgroundScatterView.Items.Remove(gf);
        }

        /// <summary>
        /// Increase the ZIndex of the map frame representing this bookmark object to bring it to front of other widgets.
        /// </summary>
        /// <param name="bookmark">Bookmark object related to the map frame.</param>
        public void BringMapFrameToFront(Bookmark bookmark)
        {
            if (bookmark.MapFrameCurrentlyOpen == true)
            {
                MapFrame mf = GetMapFrame(bookmark);
                if (mf != null)
                    mf.ZIndex = 10;
            }                
        }
        
        /// <summary>
        /// Add the bookmark list widget to the background layer.
        /// </summary>
        /// <param name="pt">Location where the list should be placed</param>
        /// <param name="orientation">Orientation of the bookmark list widget</param>
        /// <returns></returns>
        public BookmarkListFrame AddBookmarkListFrame(Point pt, double orientation)
        {
            BookmarkListFrame ttlFrame = new BookmarkListFrame(pt, orientation);
            bookmarkListFramesList.Add(ttlFrame);

            BackgroundScatterView.Items.Add(ttlFrame);

            return ttlFrame;
        }


        /// <summary>
        /// Removes a BookmarkListFrame, if it already exists on the layer.
        /// </summary>
        /// <param name="deleteFrame">BookmarkListFrame to be deleted</param>
        public void RemoveBookmarkListFrame(BookmarkListFrame deleteFrame)
        {
            if (bookmarkListFramesList.Contains(deleteFrame))
                bookmarkListFramesList.Remove(deleteFrame);
            if (BackgroundScatterView.Items.Contains(deleteFrame))
                BackgroundScatterView.Items.Remove(deleteFrame);
        }

        /// <summary>
        /// Change the visibility of the Map Layers Manager widget to be hidden
        /// </summary>
        public void HideMapLayersManagerPanel()
        {
            _mapLayersManagerPanel.Visibility = Visibility.Hidden;            
        }

        /// <summary>
        /// Change the visibility of the Map Layers Manager widget to be visible
        /// </summary>
        public void ShowMapLayersManagerPanel(Point pt, double orientation)
        {
            _mapLayersManagerPanel.Center = pt;
            _mapLayersManagerPanel.Orientation = orientation;
            _mapLayersManagerPanel.Visibility = Visibility.Visible;
            //_mapLayersManagerPanel.ZIndex = 10;
        }

        /// <summary>
        /// Add a bookmark info tip to the background layer
        /// </summary>
        /// <param name="mapTip">info tip widget to be added</param>
        /// <param name="screen_point">location of addition</param>
        /// <param name="orientation">orientation of the widget</param>
        public void AddMapTip(BookmarkInfoTip mapTip, Point screen_point, double orientation)
        {
            mapTip.Orientation = orientation;
            mapTip.Center = screen_point;

            BackgroundScatterView.Items.Add(mapTip);    
        }
        


        public void AddScatterViewItem(ScatterViewItem item)
        {
            BackgroundScatterView.Items.Add(item);
        }

        public void RemoveScatterViewItem(ScatterViewItem item)
        {
            BackgroundScatterView.Items.Remove(item);  

        }

        /// <summary>
        /// This method will handle the functionality
        /// when the "Search" button is clicked.
        /// </summary>
        /// <param name="loc"></param>
        public void AddSearchBox(Point loc)
        {
            //ScatterViewItem container = new ScatterViewItem();
            //SurfaceKeyboard.CenterX = (float)loc.X;
            //SurfaceKeyboard.CenterY = (float)loc.Y;
            //SurfaceKeyboard.IsVisible = true;
            //AddMapAnnotation(loc);

        }

        #endregion

    }
}
